package com.baoji.auth.filter;

import com.alibaba.fastjson.JSONObject;
import com.baoji.auth.model.AccountErrorCode;
import com.baoji.auth.utils.JwtTokenUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * @Date: 2021/09/24/16:44
 * @Description: Token 校验拦截器
 * <p>
 * 验证成功当然就是进行鉴权了
 * 登录成功之后走此类进行鉴权操作
 */

public class JWTAuthorizationFilter extends BasicAuthenticationFilter {

    public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {

        super(authenticationManager);
    }


    // 在过滤之前和之后执行的事情(解析校验token、判断token是否过期)
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {

        String tokenHeader = request.getHeader(JwtTokenUtil.TOKEN_HEADER);
        // 若请求头中没有 Authorization信息，或是 Authorization 不以 Bearer 开头 则直接放行
        if (tokenHeader == null || !tokenHeader.startsWith(JwtTokenUtil.TOKEN_PREFIX)) {
            chain.doFilter(request, response);
            return;
        }

        // 去掉前缀 获取真正的 token 字符串
        String token = tokenHeader.replace(JwtTokenUtil.TOKEN_PREFIX, "");
        // 校验、解析 token
        Jws<Claims> claims = JwtTokenUtil.checkJWT(token);
        if (token == null || claims == null) {
            // token 不合法
            writeJson(response, AccountErrorCode.CREDENTIALS_EXPIRED.getMessage());
            return;
        }

        // token是否过期
        if (JwtTokenUtil.isExpiration(claims)) {
            // 处理过期
            handleTokenExpired(response, request, chain);
            return;
        }

        // 构建认证信息，构建Authentication对象
        SecurityContextHolder.getContext().setAuthentication(getAuthentication(claims));
        super.doFilterInternal(request, response, chain);
    }


    //处理token过期情况
    private void handleTokenExpired(HttpServletResponse response, HttpServletRequest request, FilterChain chain) throws IOException, ServletException {
        // 获取Refresh-Token
        String refreshTokenHeader = request.getHeader(JwtTokenUtil.REFRESH_TOKEN);
        // 检测 refresh-token 是否是我们系统中签发的
        if (refreshTokenHeader == null || !refreshTokenHeader.startsWith(JwtTokenUtil.TOKEN_PREFIX)) {
            writeJson(response, AccountErrorCode.TOKEN_EXPIRED_ERROR.getMessage());
            return;
        }

        // 去掉前缀 获取真正的 refreshToken 字符串
        String refreshToken = refreshTokenHeader.replace(JwtTokenUtil.TOKEN_PREFIX, "");
        // 校验解析 refreshToken
        Jws<Claims> claims = JwtTokenUtil.checkJWT(refreshToken);
        if (refreshToken == null || claims == null) {
            // refreshToken 不合法;
            writeJson(response, AccountErrorCode.CREDENTIALS_EXPIRED.getMessage());
            return;
        }

        // 判断 refreshToken 是否过期
        if (JwtTokenUtil.isExpiration(claims)) {
            // refresh token 过期了
            writeJson(response, AccountErrorCode.CREDENTIALS_EXPIRED.getMessage());
            return;
        }

        // 重新签发 Token
        String username = JwtTokenUtil.getUsername(claims);
        String role = JwtTokenUtil.getUserRole(claims);
        String newToken = JwtTokenUtil.createToken(username, role, JwtTokenUtil.TOKEN_EXPIRITION);
        response.setHeader("token", JwtTokenUtil.TOKEN_PREFIX + newToken);

        // 设置认证信息，构建Authentication对象
        Jws<Claims> jws = JwtTokenUtil.checkJWT(newToken);
        SecurityContextHolder.getContext().setAuthentication(getAuthentication(jws));
        super.doFilterInternal(request, response, chain);
    }

    /**
     * Token对象的用户名和权限信息
     * 带用户名和密码以及权限的 Authentication
     */
    private UsernamePasswordAuthenticationToken getAuthentication(Jws<Claims> claims) {

        // 从Token中解密获取用户名
        String username = JwtTokenUtil.getUsername(claims);
        // 从Token中解密获取用户角色
        String role = JwtTokenUtil.getUserRole(claims);
        // 将[ROLE_XXX,ROLE_YYY]格式的角色字符串转换为数组
        String[] roles = StringUtils.strip(role, "[]").split(", ");
        Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
        for (String s : roles) {
            authorities.add(new SimpleGrantedAuthority(s));
        }
        if (username != null) {
            return new UsernamePasswordAuthenticationToken(username, null, authorities);
        }
        return null;
    }


    // 写 json 数据给前端
    private void writeJson(HttpServletResponse response, String msg) throws IOException, ServletException {
        // 返回json
        response.setContentType("application/json; charset=UTF-8");
        // 状态码
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        Map<String, String> params = new HashMap<>(4);
        params.put("msg", msg);
        // 写出
        response.getWriter().print(JSONObject.toJSONString(params));
    }
}
